import 'dart:async';

import 'package:flutter/material.dart';
import 'package:isolate/isolate.dart';

class JankWidget extends StatefulWidget {
  const JankWidget({super.key});

  @override
  State<JankWidget> createState() => _JankWidgetState();
}

class _JankWidgetState extends State<JankWidget> {
  int _count = 0;

  /// 为什么卡顿： 因为 Future 仍然是在同一个UI线程中做运算，异步只是在同一个线程的并发操作，仍会阻塞UI的刷新。
  /// 解决：Flutter team 提供了两种方式让我们将计算移到新的线程中，compute 和 Isolate:
  //
  // compute 轻量级，但它没有办法多次返回结果，也没有办法持续性的传值计算，每次调用，相当于新建一个隔离，如果调用过多的话反而会适得其反。
  // Isolate 消耗较重，除了创建耗时，每次创建还至少需要2Mb的空间。有OOM的风险。
  @pragma('vm:entry-point')
  static Future<int> asyncCountEven(int num) async {
    int count = 0;
    while (num > 0) {
      if (num % 2 == 0) {
        count++;
      }
      num--;
    }
    return count;
  }

  void doMockTimeConsume() async {
    // _count = await asyncCountEven(100000000);
    _count =
        await ISOManager.loadBalanceFuture<int, int>(asyncCountEven, 100000000);
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          const SizedBox(
            width: 40,
            height: 40,
            child: CircularProgressIndicator(),
          ),
          Text('$_count'),
          TextButton(
              onPressed: () {
                doMockTimeConsume();
              },
              child: const Text('耗时任务'))
        ],
      ),
    );
  }
}

/// https://github.com/awesome-tips/Flutter-Tips/blob/master/articles/Flutter%20%20%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E2%80%94%E2%80%94%E5%A6%82%E4%BD%95%E9%81%BF%E5%85%8D%E5%BA%94%E7%94%A8%20jank.md
abstract class ISOManager {
  static int isoBalanceSize = 2;
  static final Future<LoadBalancer> _loadBalancer =
      LoadBalancer.create(isoBalanceSize, IsolateRunner.spawn);

  static Future<R> loadBalanceFuture<R, P>(
    FutureOr<R> Function(P argument) function,
    P params,
  ) async {
    final lb = await _loadBalancer;
    return lb.run<R, P>(function, params);
  }
}
